package typage;

import java.util.*;

import errors.TypingError;

import ast.*;

/* phases 1 et 2 du typage
 * - phase 1: declaration des classes
 * - phase 2: heritage, declaration des champs
 */
class Typing12 extends EmptyVisitor {

    void error(String m) throws TypingError { 
	throw new TypingError(m); 
    }
    void error(Loc l, String m) throws TypingError {
	throw new TypingError(l, m); 
    }

    void check_type(Type t) throws TypingError {
	String s = t.ty_val;
	if (Misc.base_type(s)) return;
	if (!SymbolTable.classes.containsKey(s))
	    error(t.ty_loc, "no such class " + s);
    }

    JmmClass current_class;

    public void visit(Fattribute f) throws TypingError {
	check_type(f.attr_ty);
	HashMap attr = current_class.attributes;
	for (Iterator it = f.attr_ids.iterator(); it.hasNext();) {
	    Ident id = (Ident)it.next();
	    if (SymbolTable.find_attribute(current_class.class_id, id.id_val) != null)
		error(id.id_loc, "already an attribute of that name");
	    Attribute v = new Attribute(id, f.attr_ty);
	    attr.put(id.id_val, v);
	}
    }

    void check_parameters(VarList l) {
	HashSet seen = new HashSet();
	for (Iterator it = l.iterator(); it.hasNext();) {
	    Var v = (Var)it.next();
	    check_type(v.var_ty);
	    if (seen.contains(v.var_id.id_val))
		error(v.var_id.id_loc, "duplicate parameter");
	    seen.add(v.var_id.id_val);
	}
    }
			      
    public void visit(Fconstructor f) throws TypingError {
	if(!f.cstr_id.id_val.equals(current_class.class_id))
	    error(f.cstr_id.id_loc, 
		  "constructor for class "+current_class.class_id+" expected");
	check_parameters(f.cstr_args);
	Profile p = new Profile(f.cstr_args);
	HashSet cstr = current_class.constructors;
	if (cstr.contains(p))
	    error(f.cstr_id.id_loc, "duplicate constructor declaration");
	cstr.add(p);
    }
			      
    public void visit(Fmethod f) throws TypingError {
	check_type(f.meth_ty);
	check_parameters(f.meth_args);
	Profile p = new Profile(f.meth_args);
	HashMap meth = current_class.methods;
	String m = f.meth_id.id_val;
	if (!meth.containsKey(m)) meth.put(m, new HashSet());
	HashSet meth_m = (HashSet)meth.get(m);
	if (meth_m.contains(p))
	    error(f.meth_id.id_loc, "duplicate method declaration");
	meth_m.add(p);
	SymbolTable.method_ty.put
	    (SymbolTable.method_name(current_class.class_id + "_" + m, p),
	     f.meth_ty.ty_val);
    }

    public void visit(JmmFile f) throws TypingError {
	// phase 1: declaration des classes
	for (Iterator it = f.classes.iterator(); it.hasNext();) {
	    JmmClass c = (JmmClass)it.next();
	    if (SymbolTable.classes.containsKey(c.class_id)) 
		error(c.class_loc, "already a class of that name");
	    SymbolTable.add_new_class(c);
	}
	// phase 2: definition des classes (attributs, constructeurs, methodes)
	for (Iterator it = f.classes.iterator(); it.hasNext();) {
	    JmmClass c = (JmmClass)it.next();
	    current_class = c;
	    // inherits
	    String s1 = c.class_inh.id_val;
	    JmmClass c1 = (JmmClass)SymbolTable.classes.get(s1);
	    if (c1 == null)
		error(c.class_inh.id_loc, "no such class");
	    if (SymbolTable.subclass(s1, c.class_id))
	    	error(c.class_loc, "cyclic inheritance");
	    c.super_class = c1;
	    // champs
	    for (Iterator it1 = c.class_fields.iterator(); it1.hasNext();)
		((Field)it1.next()).accept(this);
	}

    }

}
